/*******************************************************************\
| Vector4.h
|
|--------------------------------------------------------------------
| Copyright 2005. Martin Fleisz.
| All rights reserved.
|
|--------------------------------------------------------------------
| CREATED:		2006/2/21
| AUTHOR:		Martin Fleisz
|
|--------------------------------------------------------------------
| DESCRIPTION:
| 
| 4 component vector class
\********************************************************************/
#ifndef VECTOR4_H
#define VECTOR4_H

#include "MathBase.h"
#include "Vector3.h"

namespace Math
{

//*****************************************************************************
// class overview:
// @short:
//  4 component vector class
// @detailed:
//  Vector class for vector calculations in three dimensional space.
//-----------------------------------------------------------------------------
class Vector4
{
	public:		
		// the vector's components
		union
		{
			struct
			{
				float m_X;
				float m_Y;
				float m_Z;
				float m_W;
			};
			float m_Xyzw[4];
		};

	public:
		// constructors
		inline Vector4();
		explicit inline Vector4(const float* p_Xyzw);
		explicit inline Vector4(const Vector3& p_Vec);
		inline Vector4(const Vector4& p_Vec);
		inline Vector4(const Vector3& p_Vec, const float p_W);
		inline Vector4(const float p_X, const float p_Y, const float p_Z);
		inline Vector4(const float p_X, const float p_Y, const float p_Z, const float p_W);

	public:
		// +/- operator (vector sign)
		inline Vector4		operator+() const;
		inline Vector4		operator-() const;

		// +=, -=, *=, /= operators
		inline Vector4&	operator+=(const Vector4& p_Vec);
		inline Vector4&	operator+=(const Vector3& p_Vec);
		inline Vector4&	operator-=(const Vector4& p_Vec);
		inline Vector4&	operator-=(const Vector3& p_Vec);
		inline float	operator*=(const Vector4& p_Vec);
		inline Vector4&	operator*=(const float p_Value);
		inline Vector4&	operator/=(const float p_Value);

		// vector addition, substraction and dot product
		inline friend const Vector4	operator+(const Vector4& p_Vec1, const Vector4& p_Vec2);
		inline friend const Vector4	operator+(const Vector4& p_Vec1, const Vector3& p_Vec2);
		inline friend const Vector4	operator+(const Vector3& p_Vec1, const Vector4& p_Vec2);

		inline friend const Vector4	operator-(const Vector4& p_Vec1, const Vector4& p_Vec2);
 		inline friend const Vector4	operator-(const Vector4& p_Vec1, const Vector3& p_Vec2);
 		inline friend const Vector4	operator-(const Vector3& p_Vec1, const Vector4& p_Vec2);

		inline friend float			operator*(const Vector4& p_Vec1, const Vector4& p_Vec2);

		// scalar multiplication
		inline friend const Vector4	operator*(const Vector4& p_Vec, const float p_Value);
		inline friend const Vector4	operator*(const float p_Value, const Vector4& p_Vec);

		// scalar division
		inline friend const Vector4	operator/(const Vector4& p_Vec, const float p_Value);
		inline friend const Vector4	operator/(const float p_Value, const Vector4& p_Vec);

		// assignment operators
		inline Vector4&	operator=(const Vector4& p_Vec);
		inline Vector4&	operator=(const Vector3& p_Vec);

		// compare operators
		inline friend bool	operator==(const Vector4& p_Vec1, const Vector4& p_Vec2);
		inline friend bool	operator<(const Vector4& p_Vec1, const Vector4& p_Vec2);

	public:

		// convert to Vector3
		inline operator Vector3() const;

		// set the vector's components
		inline void		Set(const float* p_Xyzw);
		inline void		Set(const Vector3& p_Vec);
		inline void		Set(const Vector4& p_Vec);
		inline void		Set(const float p_X, const float p_Y, const float p_Z);
		inline void		Set(const float p_X, const float p_Y, const float p_Z, const float p_W);

		// zeros the vector
		inline void		Zero();

		// return length of the vector (or square of the length for LengthSqr)
		inline float	Length()	const;
		inline float	LengthSqr()	const;        

		// normalize vector (the Fast function uses the reciprocal sqrt function
		// but is not as accurate as the standard Normalize function)
		inline void		Normalize();
		inline void		NormalizeFast();

		// dot product functions
		inline friend float	Dot4(const Vector4& p_Vec1, const Vector4& p_Vec2);
		inline friend float	Dot3(const Vector4& p_Vec1, const Vector4& p_Vec2);
		inline friend float	Dot3(const Vector4& p_Vec1, const Vector3& p_Vec2);
		inline friend float	Dot3(const Vector3& p_Vec1, const Vector4& p_Vec2);

		// interpolates p_Vec1 and p_Vec2 and stores the result in the current vector
		inline void		Interpolate(const Vector4& p_Vec1, const Vector4& p_Vec2, const float p_Value);

	public:
		// special predefined values
		static const Vector4 ZERO;
		static const Vector4 INFINITY;
		static const Vector4 UNIT_X;
		static const Vector4 UNIT_Y;
		static const Vector4 UNIT_Z;
		static const Vector4 UNIT_W;
};

/* ***************************************************************** *\
|  INLINE FUNCTIONS
\* ***************************************************************** */
inline Vector4::Vector4() 
{ 
}

inline Vector4::Vector4(const float p_X, const float p_Y, const float p_Z) 
{ 
	m_X = p_X;
	m_Y = p_Y;
	m_Z = p_Z;
	m_W = 0.0f;
}

inline Vector4::Vector4(const float p_X, const float p_Y, const float p_Z, const float p_W) 
{ 
	m_X = p_X;
	m_Y = p_Y;
	m_Z = p_Z;
	m_W = p_W;
}

inline Vector4::Vector4(const float* p_Xyzw) 	
{ 
	m_X = p_Xyzw[0];
	m_Y = p_Xyzw[1];
	m_Z = p_Xyzw[2];
	m_W = p_Xyzw[3];
}

inline Vector4::Vector4(const Vector3& p_Vec)
{
	m_X = p_Vec.m_X;
	m_Y = p_Vec.m_Y;
	m_Z = p_Vec.m_Z;
	m_W = 0.0f;
}

inline Vector4::Vector4(const Vector3& p_Vec, const float p_W)
{
	m_X = p_Vec.m_X;
	m_Y = p_Vec.m_Y;
	m_Z = p_Vec.m_Z;
	m_W = p_W;
}

inline Vector4::Vector4(const Vector4& p_Vec)
{
	m_X = p_Vec.m_X;
	m_Y = p_Vec.m_Y;
	m_Z = p_Vec.m_Z;
	m_W = p_Vec.m_W;
}

// + operator (vector sign)
inline Vector4 Vector4::operator+() const
{
	return (*this);
}

// - operator (vector sign)
inline Vector4 Vector4::operator-() const
{
	return Vector4(-m_X, -m_Y, -m_Z, -m_W);
}

// += operator
inline Vector4& Vector4::operator+=(const Vector4& p_Vec)
{
	m_X += p_Vec.m_X;
	m_Y += p_Vec.m_Y;
	m_Z += p_Vec.m_Z;
	m_W += p_Vec.m_W;

	return (*this);
}

inline Vector4& Vector4::operator+=(const Vector3& p_Vec)
{
	m_X += p_Vec.m_X;
	m_Y += p_Vec.m_Y;
	m_Z += p_Vec.m_Z;

	return (*this);
}

// -= operator
inline Vector4& Vector4::operator-=(const Vector4& p_Vec)
{
	m_X -= p_Vec.m_X;
	m_Y -= p_Vec.m_Y;
	m_Z -= p_Vec.m_Z;
	m_W -= p_Vec.m_W;

	return (*this);
}

inline Vector4& Vector4::operator-=(const Vector3& p_Vec)
{
	m_X -= p_Vec.m_X;
	m_Y -= p_Vec.m_Y;
	m_Z -= p_Vec.m_Z;

	return (*this);
}

// dot product
inline float Vector4::operator*=(const Vector4& p_Vec)
{
	return ((m_X * p_Vec.m_X) + (m_Y * p_Vec.m_Y) + (m_Z * p_Vec.m_Z) + (m_W * p_Vec.m_W));
}

// scalar multiplication
inline Vector4& Vector4::operator*=(const float p_Value)
{
	m_X *= p_Value;
	m_Y *= p_Value;
	m_Z *= p_Value;
	m_W *= p_Value;

	return (*this);
}

// /= operator
inline Vector4& Vector4::operator/=(const float p_Value)
{
	const float invValue = 1.0f / p_Value;
	m_X *= invValue;
	m_Y *= invValue;
	m_Z *= invValue;
	m_W *= invValue;

	return (*this);
}

// assignment operator
inline Vector4& Vector4::operator=(const Vector4& p_Vec)
{
	m_X = p_Vec.m_X;
	m_Y = p_Vec.m_Y;
	m_Z = p_Vec.m_Z;
	m_W = p_Vec.m_W;

	return (*this);
}

// assignment operator for Vector3
inline Vector4& Vector4::operator=(const Vector3& p_Vec)
{
	m_X = p_Vec.m_X;
	m_Y = p_Vec.m_Y;
	m_Z = p_Vec.m_Z;

	return (*this);
}

// friend function for addition
inline const Vector4 operator+(const Vector4& p_Vec1, const Vector4& p_Vec2)
{
	return Vector4(p_Vec1.m_X + p_Vec2.m_X, p_Vec1.m_Y + p_Vec2.m_Y, p_Vec1.m_Z + p_Vec2.m_Z, p_Vec1.m_W + p_Vec2.m_W);
}

inline const Vector4 operator+(const Vector4& p_Vec1, const Vector3& p_Vec2)
{
	return Vector4(p_Vec1.m_X + p_Vec2.m_X, p_Vec1.m_Y + p_Vec2.m_Y, p_Vec1.m_Z + p_Vec2.m_Z, p_Vec1.m_W);
}

inline const Vector4 operator+(const Vector3& p_Vec1, const Vector4& p_Vec2)
{
	return Vector4(p_Vec1.m_X + p_Vec2.m_X, p_Vec1.m_Y + p_Vec2.m_Y, p_Vec1.m_Z + p_Vec2.m_Z, p_Vec2.m_W);
}

// substraction
inline const Vector4 operator-(const Vector4& p_Vec1, const Vector4& p_Vec2)
{
	return Vector4(p_Vec1.m_X - p_Vec2.m_X, p_Vec1.m_Y - p_Vec2.m_Y, p_Vec1.m_Z - p_Vec2.m_Z, p_Vec1.m_W - p_Vec2.m_W);
}

inline const Vector4 operator-(const Vector4& p_Vec1, const Vector3& p_Vec2)
{
	return Vector4(p_Vec1.m_X - p_Vec2.m_X, p_Vec1.m_Y - p_Vec2.m_Y, p_Vec1.m_Z - p_Vec2.m_Z, p_Vec1.m_W);
}

inline const Vector4 operator-(const Vector3& p_Vec1, const Vector4& p_Vec2)
{
	return Vector4(p_Vec1.m_X - p_Vec2.m_X, p_Vec1.m_Y - p_Vec2.m_Y, p_Vec1.m_Z - p_Vec2.m_Z, p_Vec2.m_W);
}

// dot product
inline float operator*(const Vector4& p_Vec1, const Vector4& p_Vec2)
{
	return ((p_Vec1.m_X * p_Vec2.m_X) + (p_Vec1.m_Y * p_Vec2.m_Y) + (p_Vec1.m_Z * p_Vec2.m_Z) + (p_Vec1.m_W * p_Vec2.m_W));
}

// vector/scalar multiplication
inline const Vector4 operator*(const Vector4& p_Vec, const float p_Value)
{
	return Vector4(p_Vec.m_X * p_Value, p_Vec.m_Y * p_Value, p_Vec.m_Z * p_Value, p_Vec.m_W * p_Value);
}

inline const Vector4 operator*(const float p_Value, const Vector4& p_Vec)
{
	return Vector4(p_Vec.m_X * p_Value, p_Vec.m_Y * p_Value, p_Vec.m_Z * p_Value, p_Vec.m_W * p_Value);
}

// vector/scalar division
inline const Vector4 operator/(const Vector4& p_Vec, const float p_Value)
{
	const float invValue = 1.0f / p_Value;
	return Vector4(p_Vec.m_X * invValue, p_Vec.m_Y * invValue, p_Vec.m_Z * invValue, p_Vec.m_W * invValue);
}

inline const Vector4 operator/(const float p_Value, const Vector4& p_Vec)
{
	const float invValue = 1.0f / p_Value;
	return Vector4(p_Vec.m_X * invValue, p_Vec.m_Y * invValue, p_Vec.m_Z * invValue, p_Vec.m_W * invValue);
}

// comparison operators
inline bool operator==(const Vector4& p_Vec1, const Vector4& p_Vec2)
{
	if( Abs(p_Vec1.m_X - p_Vec2.m_X) < EPSILON && Abs(p_Vec1.m_Y - p_Vec2.m_Y) < EPSILON && 
		Abs(p_Vec1.m_Z - p_Vec2.m_Z) < EPSILON && Abs(p_Vec1.m_W - p_Vec2.m_W) < EPSILON)
		return true;

	return false;
}

inline bool operator<(const Vector4& p_Vec1, const Vector4& p_Vec2)
{
	if(p_Vec1.LengthSqr() < p_Vec2.LengthSqr())
		return true;

	return false;
}

// convert to Vector3
inline Vector4::operator Vector3() const
{
	return Vector3(m_X, m_Y, m_Z);
}

// set vector from a float pointer
inline void Vector4::Set(const float* p_Xyzw)
{
	m_X = p_Xyzw[0];
	m_Y = p_Xyzw[1];
	m_Z = p_Xyzw[2];
	m_W = p_Xyzw[3];
}

// copy vector from another vector
inline void Vector4::Set(const Vector4& p_Vec)
{
	m_X = p_Vec.m_X;
	m_Y = p_Vec.m_Y;
	m_Z = p_Vec.m_Z;
	m_W = p_Vec.m_W;
}

// copy vector from a vector3
inline void Vector4::Set(const Vector3& p_Vec)
{
	m_X = p_Vec.m_X;
	m_Y = p_Vec.m_Y;
	m_Z = p_Vec.m_Z;
	m_W = 0.0f;
}

// set vector components
inline void Vector4::Set(const float p_X, const float p_Y, const float p_Z)
{
	m_X = p_X;
	m_Y = p_Y;
	m_Z = p_Z;
	m_W = 0.0f;
}

inline void Vector4::Set(const float p_X, const float p_Y, const float p_Z, const float p_W)
{
	m_X = p_X;
	m_Y = p_Y;
	m_Z = p_Z;
	m_W = p_W;
}

// zeros all vector components
inline void Vector4::Zero()
{
	m_X = m_Y = m_Z = m_W = 0.0f;
}

// length function
inline float Vector4::Length() const
{
	return Sqrt(Sqr(m_X) + Sqr(m_Y) + Sqr(m_Z) + Sqr(m_W));
}

// returns the square of the length (used for == comparison)
inline float Vector4::LengthSqr() const
{
	return Sqr(m_X) + Sqr(m_Y) + Sqr(m_Z) + Sqr(m_W);
}

// normalizes the vector (make a unit vector)
inline void Vector4::Normalize()
{
	const float length = Length();
	if(length > Math::EPSILON)
	{
		const float invLength = 1.0f / length;
		m_X *= invLength;
		m_Y *= invLength;
		m_Z *= invLength;
		m_W *= invLength;
	}
}

// normalizes the vector (make a unit vector) using the fast reciprocal sqrt function
inline void Vector4::NormalizeFast()
{
	const float invLength = InvSqrt(LengthSqr());
	m_X *= invLength;
	m_Y *= invLength;
	m_Z *= invLength;
	m_W *= invLength;
}

// interpolate (between 0.0 and 1.0) the two vectors and store the result in the current intance
inline void Vector4::Interpolate(const Vector4& p_Vec1, const Vector4& p_Vec2, const float p_Value)
{
	const float oneMinusValue = 1.0f - p_Value;

	m_X = (p_Vec1.m_X * oneMinusValue) + (p_Vec2.m_X * p_Value);
	m_Y = (p_Vec1.m_Y * oneMinusValue) + (p_Vec2.m_Y * p_Value);
	m_Z = (p_Vec1.m_Z * oneMinusValue) + (p_Vec2.m_Z * p_Value);
	m_W = (p_Vec1.m_W * oneMinusValue) + (p_Vec2.m_W * p_Value);
}

// dot3 product with a Vector4
inline float Dot4(const Vector4& p_Vec1, const Vector4& p_Vec2)
{
	return (p_Vec1.m_X * p_Vec2.m_X) + (p_Vec1.m_Y * p_Vec2.m_Y) + (p_Vec1.m_Z * p_Vec2.m_Z) + (p_Vec1.m_W * p_Vec2.m_W);
}

inline float Dot3(const Vector4& p_Vec1, const Vector4& p_Vec2)
{
	return (p_Vec1.m_X * p_Vec2.m_X) + (p_Vec1.m_Y * p_Vec2.m_Y) + (p_Vec1.m_Z * p_Vec2.m_Z);
}

inline float Dot3(const Vector4& p_Vec1, const Vector3& p_Vec2)
{
	return (p_Vec1.m_X * p_Vec2.m_X) + (p_Vec1.m_Y * p_Vec2.m_Y) + (p_Vec1.m_Z * p_Vec2.m_Z);
}

inline float Dot3(const Vector3& p_Vec1, const Vector4& p_Vec2)
{
	return (p_Vec1.m_X * p_Vec2.m_X) + (p_Vec1.m_Y * p_Vec2.m_Y) + (p_Vec1.m_Z * p_Vec2.m_Z);
}


} // namespace Math

#endif // VECTOR4_H
